use codegraph_core::{FileWatcher, IncrementalParseEngine, WatcherConfig, FileChangeType};
use std::sync::Arc;
use std::time::Duration;
use tempfile::TempDir;
use tokio::time::sleep;

#[tokio::test]
async fn test_file_watcher_creation() {
    let engine = Arc::new(IncrementalParseEngine::new());
    let watcher = FileWatcher::new(engine);
    
    let stats = watcher.get_stats().await;
    assert_eq!(stats.tracked_files_count, 0);
    assert!(!stats.is_watching);
}

#[tokio::test]
async fn test_file_watcher_with_config() {
    let engine = Arc::new(IncrementalParseEngine::new());
    let config = WatcherConfig {
        debounce_delay_ms: 50,
        max_batch_size: 10,
        watched_extensions: vec![".py".to_string(), ".rs".to_string()],
        ignored_directories: vec![".git".to_string()],
        recursive: true,
        process_initial_scan: false,
    };
    
    let watcher = FileWatcher::with_config(engine, config);
    
    let stats = watcher.get_stats().await;
    assert_eq!(stats.tracked_files_count, 0);
    assert!(!stats.is_watching);
}

#[tokio::test]
async fn test_file_watcher_start_stop() {
    let engine = Arc::new(IncrementalParseEngine::new());
    let mut watcher = FileWatcher::new(engine);
    
    // Create a temporary directory
    let temp_dir = TempDir::new().expect("Failed to create temp directory");
    let temp_path = temp_dir.path();
    
    // Start watching
    let result = watcher.start_watching(temp_path).await;
    assert!(result.is_ok(), "Failed to start watching: {:?}", result);
    
    let stats = watcher.get_stats().await;
    assert!(stats.is_watching);
    
    // Stop watching
    let result = watcher.stop_watching().await;
    assert!(result.is_ok(), "Failed to stop watching: {:?}", result);
    
    let stats = watcher.get_stats().await;
    assert!(!stats.is_watching);
    assert_eq!(stats.tracked_files_count, 0);
}

#[tokio::test]
async fn test_file_change_detection() {
    let engine = Arc::new(IncrementalParseEngine::new());
    let config = WatcherConfig {
        debounce_delay_ms: 10,
        max_batch_size: 5,
        watched_extensions: vec![".py".to_string()],
        ignored_directories: vec![],
        recursive: true,
        process_initial_scan: false,
    };
    
    let mut watcher = FileWatcher::with_config(engine, config);
    
    // Create a temporary directory
    let temp_dir = TempDir::new().expect("Failed to create temp directory");
    let temp_path = temp_dir.path();
    
    // Start watching
    watcher.start_watching(temp_path).await.expect("Failed to start watching");
    
    // Create a Python file
    let test_file = temp_path.join("test.py");
    tokio::fs::write(&test_file, r#"
def hello():
    print("Hello, World!")

hello()
"#).await.expect("Failed to write test file");
    
    // Give the watcher some time to detect the change
    sleep(Duration::from_millis(100)).await;
    
    // Process events
    let result = watcher.process_events().await.expect("Failed to process events");
    
    println!("File change detection test results:");
    println!("  Files processed: {}", result.files_processed);
    println!("  Successful updates: {}", result.successful_updates);
    println!("  Failed updates: {}", result.failed_updates);
    println!("  Processing time: {}ms", result.processing_time_ms);
    
    for file_result in &result.file_results {
        println!("  File: {:?}", file_result.file_path);
        println!("    Success: {}", file_result.success);
        if let Some(error) = &file_result.error {
            println!("    Error: {}", error);
        }
    }
    
    // Note: The actual file change detection depends on the file system
    // and timing, so we mainly test that the watcher doesn't crash
    
    // Stop watching
    watcher.stop_watching().await.expect("Failed to stop watching");
}

#[tokio::test]
async fn test_language_detection() {
    let engine = Arc::new(IncrementalParseEngine::new());
    let watcher = FileWatcher::new(engine);
    
    // Test language detection (this is a private method, so we test indirectly)
    // by checking the file extension filtering logic
    
    let temp_dir = TempDir::new().expect("Failed to create temp directory");
    let temp_path = temp_dir.path();
    
    // Create files with different extensions
    let python_file = temp_path.join("test.py");
    let rust_file = temp_path.join("test.rs");
    let javascript_file = temp_path.join("test.js");
    let text_file = temp_path.join("test.txt");
    
    tokio::fs::write(&python_file, "print('hello')").await.expect("Failed to write Python file");
    tokio::fs::write(&rust_file, "fn main() {}").await.expect("Failed to write Rust file");
    tokio::fs::write(&javascript_file, "console.log('hello')").await.expect("Failed to write JS file");
    tokio::fs::write(&text_file, "hello world").await.expect("Failed to write text file");
    
    // The watcher should process .py, .rs, .js files but not .txt files
    // This is tested implicitly through the should_process_file logic
    
    println!("Language detection test completed");
    println!("Created test files:");
    println!("  Python: {:?}", python_file);
    println!("  Rust: {:?}", rust_file);
    println!("  JavaScript: {:?}", javascript_file);
    println!("  Text: {:?}", text_file);
}

#[tokio::test]
async fn test_ignored_directories() {
    let engine = Arc::new(IncrementalParseEngine::new());
    let config = WatcherConfig {
        debounce_delay_ms: 10,
        max_batch_size: 5,
        watched_extensions: vec![".py".to_string()],
        ignored_directories: vec![".git".to_string(), "node_modules".to_string()],
        recursive: true,
        process_initial_scan: false,
    };
    
    let watcher = FileWatcher::with_config(engine, config);
    
    let temp_dir = TempDir::new().expect("Failed to create temp directory");
    let temp_path = temp_dir.path();
    
    // Create directories that should be ignored
    let git_dir = temp_path.join(".git");
    let node_modules_dir = temp_path.join("node_modules");
    
    tokio::fs::create_dir(&git_dir).await.expect("Failed to create .git directory");
    tokio::fs::create_dir(&node_modules_dir).await.expect("Failed to create node_modules directory");
    
    // Create files in ignored directories
    let git_file = git_dir.join("config.py");
    let node_file = node_modules_dir.join("package.py");
    
    tokio::fs::write(&git_file, "# git config").await.expect("Failed to write git file");
    tokio::fs::write(&node_file, "# node package").await.expect("Failed to write node file");
    
    // The watcher should ignore these files
    // This is tested through the should_process_file logic
    
    println!("Ignored directories test completed");
    println!("Created files in ignored directories:");
    println!("  Git file: {:?}", git_file);
    println!("  Node file: {:?}", node_file);
}

#[tokio::test]
async fn test_batch_processing() {
    let engine = Arc::new(IncrementalParseEngine::new());
    let config = WatcherConfig {
        debounce_delay_ms: 10,
        max_batch_size: 3, // Small batch size for testing
        watched_extensions: vec![".py".to_string()],
        ignored_directories: vec![],
        recursive: true,
        process_initial_scan: false,
    };
    
    let mut watcher = FileWatcher::with_config(engine, config);
    
    let temp_dir = TempDir::new().expect("Failed to create temp directory");
    let temp_path = temp_dir.path();
    
    // Start watching
    watcher.start_watching(temp_path).await.expect("Failed to start watching");
    
    // Create multiple files to test batch processing
    for i in 0..5 {
        let file_path = temp_path.join(format!("test_{}.py", i));
        tokio::fs::write(&file_path, format!("# Test file {}\nprint({})", i, i))
            .await
            .expect("Failed to write test file");
    }
    
    // Give the watcher time to detect changes
    sleep(Duration::from_millis(200)).await;
    
    // Process events
    let result = watcher.process_events().await.expect("Failed to process events");
    
    println!("Batch processing test results:");
    println!("  Files processed: {}", result.files_processed);
    println!("  Successful updates: {}", result.successful_updates);
    println!("  Failed updates: {}", result.failed_updates);
    println!("  Processing time: {}ms", result.processing_time_ms);
    
    // The batch size should limit how many files are processed at once
    // Note: Actual behavior depends on file system timing
    
    watcher.stop_watching().await.expect("Failed to stop watching");
}

#[tokio::test]
async fn test_watcher_stats() {
    let engine = Arc::new(IncrementalParseEngine::new());
    let watcher = FileWatcher::new(engine);
    
    // Test initial stats
    let stats = watcher.get_stats().await;
    assert_eq!(stats.tracked_files_count, 0);
    assert!(!stats.is_watching);
    
    println!("Watcher stats test:");
    println!("  Tracked files: {}", stats.tracked_files_count);
    println!("  Is watching: {}", stats.is_watching);
}
